package com.aplink.generic.util;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

import com.aplink.generic.constant.ConfigParameter;

public class RotateImageView extends ImageView {
	private static final int ANIMATION_SPEED = 180 * 2;

	private static final int NONE = 0;
	private int beginAngle = 0;
	Path clipPath = null;
	private int deltaX = 0;

	private int deltaY = 0;
	private boolean editEnable = true;
	private boolean isMoved = false;
	private boolean isRequestEffect = false;
	private int lastDeltaX = 0;
	private int lastDeltaY = 0;
	// private ArrayList touchPoints = null;
	// private boolean isMultiTouch = false;
	// private int pathEffectPhase = 0;
	// private PointF centerPoint;
	private PointF lastPoint;
	private PointF lastPointPress;
	private final PointF lastPointPressSwipe = new PointF();
	private int lastTouchDegree = -1;
	private long mAnimationEndTime = 0L;
	private long mAnimationStartTime = 0L;
	private final Matrix matrix = new Matrix();
	private final float maxScaleZoom = 5f;

	private boolean mClockwise = false;
	private int mCurrentDegree = 0;

	private final float minScale = 1f;
	private final int mode = RotateImageView.NONE;

	private int mStartDegree = 0;
	private int mTargetDegree = 0;

	private float oldDist = 0.0f;
	public boolean onLeftSide = false, onTopSide = false, onRightSide = false,
	        onBottomSide = false;

	private float origWidth, origHeight;

	private float saveScale = 1f;
	private float scaleFactor = 1.0f;
	private int scaleH = -1;
	private int scaleW = -1;
	private final boolean swipeEnable = false;

	private boolean useFitBoundMode = false;
	private boolean useRotateMode = true;
	private boolean useZoomMode = true;
	int w, h;
	private float width, height;

	//
	// private CreateNewKard parentActivity = null;
	// private CreateNewKard parentActivitySwipe = null;

	public RotateImageView(final Context context) {
		super(context);
		super.setClickable(true);
		initialize(context);
	}

	public RotateImageView(final Context context, final AttributeSet attrs) {
		super(context, attrs);
		initialize(context);
		super.setClickable(true);
	}

	public RotateImageView(final Context context, final AttributeSet attrs,
			final int defStyle) {
		super(context, attrs, defStyle);
		initialize(context);
		super.setClickable(true);
	}

	public RotateImageView(final Context context, final int defStyle) {
		super(context, null, defStyle);
		initialize(context);
		super.setClickable(true);
	}

	public void doRotate(final int degree) {
		setDegree(degree);
	}

	private int getDeltaX(final MotionEvent event) {
		return Math.round(event.getX(0) - this.lastPointPress.x);
	}

	private int getDeltaY(final MotionEvent event) {
		return Math.round(event.getY(0) - this.lastPointPress.y);
	}

	public int getImageHeight() {
		return this.scaleH;
	}

	public int getImageWidth() {
		return this.scaleW;
	}

	public int getImageX() {
		return this.lastDeltaX != -1 ? this.lastDeltaX : 0;
	}

	public int getImageY() {
		return this.lastDeltaY != -1 ? this.lastDeltaY : 0;
	}

	public boolean getSwipeEnable() {
		return this.swipeEnable;
	}

	private void initialize(final Context context) {
		// touchPoints = new ArrayList();
		this.lastPoint = new PointF();
		this.lastPoint.set(-1F, -1F);
		this.lastPointPress = new PointF();
		this.lastPointPress.set(-1F, -1F);
		this.editEnable = true;
	}

	// private void setPoints(MotionEvent event) {
	// touchPoints.clear();
	// int pointerIndex = 0;
	// for (int index = 0; index < event.getPointerCount(); index++) {
	// pointerIndex = event.getPointerId(index);
	// touchPoints.add(new PointF(event.getX(pointerIndex), event
	// .getY(pointerIndex)));
	// }
	//
	// }

	@Override
	protected void onDraw(final Canvas canvas) {
		final Drawable drawable = getDrawable();
		if (drawable == null) {
			return;
		}
		if (this.isRequestEffect && (drawable != null)) {
			this.isRequestEffect = false;
		}

		final Rect bounds2 = drawable.getBounds();
		if (this.scaleW == -1) {
			this.scaleW = getWidth();
			this.scaleH = Math
					.round((getWidth() * (bounds2.bottom + bounds2.top))
							/ (bounds2.right + bounds2.left));
			if (getHeight() > this.scaleH) {
				this.scaleH = getHeight();
				this.scaleW = Math
						.round((getHeight() * (bounds2.right + bounds2.left))
								/ (bounds2.bottom + bounds2.top));
			}
		}
		// end
		final Rect bounds = drawable.getBounds();

		this.w = Math.round(this.scaleFactor * (bounds.left + this.scaleW));
		this.h = Math.round(this.scaleFactor * (bounds.top + this.scaleH));
		drawable.setBounds(bounds.left, bounds.top, this.w, this.h);
		if ((this.w == 0) || (this.h == 0)) {
			return;
		}
		if (this.mCurrentDegree != this.mTargetDegree) {
			final long time = AnimationUtils.currentAnimationTimeMillis();
			if (time < this.mAnimationEndTime) {
				final int deltaTime = (int) (time - this.mAnimationStartTime);
				int degree = this.mStartDegree
						+ ((RotateImageView.ANIMATION_SPEED * (this.mClockwise ? deltaTime
								: -deltaTime)) / 1000);
				degree = degree < 0 ? (degree % 360) + 360 : degree % 360;
				this.mCurrentDegree = degree;
				this.invalidate();
			} else {
				this.mCurrentDegree = this.mTargetDegree;
			}
		}
		final int left = getPaddingLeft();
		final int top = getPaddingTop();
		final int right = getPaddingRight();
		final int bottom = getPaddingBottom();
		final int width = getWidth() - left - right;
		final int height = getHeight() - top - bottom;
		final int saveCount = canvas.getSaveCount();
		canvas.translate(left + (width / 2) + this.deltaX, top + (height / 2)
				+ this.deltaY);
		canvas.rotate(-this.mCurrentDegree);
		canvas.translate(-this.w >> 1, -this.h >> 1);
		drawable.draw(canvas);
		canvas.restoreToCount(saveCount);
	}

	@Override
	public boolean onTouchEvent(final MotionEvent event) {
		super.onTouchEvent(event);
		if (this.swipeEnable) {
			final int action = event.getAction() & MotionEvent.ACTION_MASK;
			switch (action) {
				case MotionEvent.ACTION_DOWN:
					this.lastPointPressSwipe.set(event.getX(0), event.getY(0));
					break;

				case MotionEvent.ACTION_POINTER_DOWN:
					break;

				case MotionEvent.ACTION_POINTER_UP:
					break;

				case MotionEvent.ACTION_UP:
					if ((this.lastPointPressSwipe.x != -1)
					        && this.isMoved
					        && (event.getX(0) > (this.lastPointPressSwipe.x - 30))) {
						// parentActivitySwipe.swipeLeftTemplate();
					}
					if ((this.lastPointPressSwipe.x != -1)
					        && this.isMoved
					        && (event.getX(0) < (this.lastPointPressSwipe.x - 30))) {
						// parentActivitySwipe.swipeRightTemplate();
					}
					this.isMoved = false;
					this.lastPointPressSwipe.set(-1F, -1F);
					break;

				case MotionEvent.ACTION_MOVE:
					this.isMoved = true;
					break;
			}
			return true;
		}
		if (!this.editEnable) {
			// TCuong 07 - 12 - 2012 return touch event for parent view - fix
			// bug KB not hide
			return false;
		}
		// end
		final int action = event.getAction() & MotionEvent.ACTION_MASK;
		try {
			switch (action) {
				case MotionEvent.ACTION_DOWN:
					this.lastPointPress.set(event.getX(0), event.getY(0));
					this.invalidate();
					break;

				case MotionEvent.ACTION_POINTER_DOWN:
					// lastPointPress.set(event.getX(1), event.getY(1));
					// isMultiTouch = true;
					this.beginAngle = rotation(event);
					this.oldDist = spacing(event);
					this.invalidate();
					break;

				case MotionEvent.ACTION_POINTER_UP:
					// isMultiTouch = false;
					this.lastPoint.set(-1F, -1F);
					// lastDeltaX = deltaX;
					// lastDeltaY = deltaY;
					this.lastPointPress.set(-1F, -1F);
					if (this.scaleW > -1) {
						if (this.scaleW > this.scaleH) {
							this.scaleW = Math.round(this.scaleFactor
							        * this.scaleH) > 10 ? Math
							        .round(this.scaleFactor * this.scaleW)
							        : (10 * getWidth()) / getHeight();
							this.scaleH = Math.round(this.scaleFactor
							        * this.scaleH) > 10 ? Math
							        .round(this.scaleFactor * this.scaleH) : 10;
						} else {
							this.scaleW = Math.round(this.scaleFactor
							        * this.scaleW) > 10 ? Math
							        .round(this.scaleFactor * this.scaleW) : 10;
							this.scaleH = Math.round(this.scaleFactor
							        * this.scaleW) > 10 ? Math
							        .round(this.scaleFactor * this.scaleH)
							        : (10 * getHeight()) / getWidth();
						}
					}
					this.scaleFactor = 1.0F;
					this.lastTouchDegree = this.mTargetDegree;
					// lastDeltaX = deltaX;
					// lastDeltaY = deltaY;
					// lastTouchDegree += mCurrentDegree;
					break;

				case MotionEvent.ACTION_UP:
					// isMultiTouch = false;
					this.lastPoint.set(-1F, -1F);
					this.lastPointPress.set(-1F, -1F);
					if (this.scaleW > -1) {
						if (this.scaleW > this.scaleH) {
							this.scaleW = Math.round(this.scaleFactor
							        * this.scaleH) > 10 ? Math
							        .round(this.scaleFactor * this.scaleW)
							        : (10 * getWidth()) / getHeight();
							this.scaleH = Math.round(this.scaleFactor
							        * this.scaleH) > 10 ? Math
							        .round(this.scaleFactor * this.scaleH) : 10;
						} else {
							this.scaleW = Math.round(this.scaleFactor
							        * this.scaleW) > 10 ? Math
							        .round(this.scaleFactor * this.scaleW) : 10;
							this.scaleH = Math.round(this.scaleFactor
							        * this.scaleW) > 10 ? Math
							        .round(this.scaleFactor * this.scaleH)
							        : (10 * getHeight()) / getWidth();
						}
					}
					this.scaleFactor = 1.0F;
					this.lastTouchDegree = this.mTargetDegree;
					this.lastDeltaX = this.deltaX;
					this.lastDeltaY = this.deltaY;
					// lastTouchDegree += mCurrentDegree;
					break;

				case MotionEvent.ACTION_MOVE:
					if (event.getPointerCount() > 1) {
						if (this.lastPoint.x == -1F) {
							this.lastPoint.set(
							        Math.abs(event.getX(0) - event.getX(1)),
							        Math.abs(event.getY(0) - event.getY(1)));
						} else {
							final int width = getWidth() - getPaddingLeft()
							        - getPaddingRight();
							final int height = getHeight() - getPaddingTop()
							        - getPaddingBottom();
							if (((((width / 2) + Math.abs(this.deltaX)) <= (Math
							        .round((spacing(event) / this.oldDist)
							                * (getDrawable().getBounds().left + this.scaleW)) >> 1)) && (((height / 2) + Math
							        .abs(this.deltaY)) <= (Math
							        .round((spacing(event) / this.oldDist)
							                * (getDrawable().getBounds().top + this.scaleH)) >> 1)))
							        || !this.useFitBoundMode) {
								if (this.useZoomMode) {
									this.scaleFactor = spacing(event)
									        / this.oldDist;
								}
							}
							// Log.d("debug degree lastTouchDegree", "  " +
							// lastTouchDegree);
							// Log.d("debug degree angle", "  " + (beginAngle -
							// rotation(event)));
							// Log.d("debug degree", "  " + (beginAngle -
							// rotation(event) + lastTouchDegree));
							// if(Math.abs(beginAngle - rotation(event)) < 180);
							if (this.useRotateMode) {
								setDegree((this.beginAngle - rotation(event))
								        + this.lastTouchDegree);// >
								// 10
								// ?
								// rotation(event)
								// + mCurrentDegree
								// :
								// mCurrentDegree);
							}
						}
					} else {
						if (this.lastPointPress.x == -1F) {
							this.lastPointPress.set(event.getX(0),
							        event.getY(0));
						} else {
							final int width = getWidth() - getPaddingLeft()
							        - getPaddingRight();
							final int height = getHeight() - getPaddingTop()
							        - getPaddingBottom();
							if ((((width / 2) + Math.abs(getDeltaX(event)
							        + this.lastDeltaX)) <= (this.w >> 1))
							        || !this.useFitBoundMode) {
								this.deltaX = getDeltaX(event)
								        + this.lastDeltaX;
							}
							if ((((height / 2) + Math.abs(getDeltaY(event)
							        + this.lastDeltaY)) <= (this.h >> 1))
							        || !this.useFitBoundMode) {
								this.deltaY = getDeltaY(event)
								        + this.lastDeltaY;
							}
						}
					}
					this.invalidate();
					break;
			}
		} catch (final Exception exception) {
		}
		return true;
	}

	public void onZoom(final boolean zoomIn) {

		float mScaleFactor = zoomIn ? 1.2f : 0.75f;

		final float origScale = this.saveScale;
		this.saveScale *= mScaleFactor;

		if (this.saveScale > this.maxScaleZoom) {
			this.saveScale = this.maxScaleZoom;
			mScaleFactor = this.maxScaleZoom / origScale;
		} else if (this.saveScale < this.minScale) {
			this.saveScale = this.minScale;
			mScaleFactor = this.minScale / origScale;
		}

		if (((this.origWidth * this.saveScale) <= this.width)
				|| ((this.origHeight * this.saveScale) <= this.height)) {
			this.matrix.postScale(mScaleFactor, mScaleFactor, this.width / 2,
					this.height / 2);
		} else {
			this.matrix.postScale(mScaleFactor, mScaleFactor, this.width / 2,
					this.height / 2);
		}

		// matrix.getValues(m);
		// calcPadding();
		// checkAndSetTranslate(0, 0);

		// Log.w(TAG, "right : " + right);
		// Log.w(TAG, "width : " + width);
		// Log.w(TAG, "bottom : " + bottom);
		// Log.w(TAG, "height : " + height);
		// Log.w(TAG, "redundantXSpace : " + redundantXSpace);
		// Log.w(TAG, "redundantYSpace : " + redundantYSpace);

		setImageMatrix(this.matrix);
		this.invalidate();
	}

	public boolean pagerCanScroll() {
		if (this.mode != RotateImageView.NONE) {
			return false;
		}
		return this.saveScale == this.minScale;
	}

	public void reset() {
		// TCuong 10 - 12 - 2012 fix scale image input from camera or local file
		this.scaleW = -1;// getWidth();
		this.scaleH = -1;// getHeight();
		// end
		setDegree(0);
		this.scaleFactor = 1.0F;
		this.lastTouchDegree = 0;
		this.lastDeltaX = 0;
		this.lastDeltaY = 0;
		this.deltaX = 0;
		this.deltaY = 0;
	}

	private int rotation(final MotionEvent event) {
		final double delta_x = event.getX(0) - event.getX(1);
		final double delta_y = event.getY(0) - event.getY(1);

		if (delta_x == 0) {
			return 0;
		} else {
			double radians;
			// if (delta_x > 0){
			radians = Math.atan2(delta_y, delta_x);
			return (int) Math.round(Math.toDegrees(radians));
			// }
			// else{
			// radians = Math.atan2(delta_y, -delta_x);
			// return 180 + (int) Math.round(Math.toDegrees(radians));
			// }
		}
	}

	public void setDefaultDrawing(final boolean enable) {
	}

	protected void setDegree(int degree) {
		// make sure in the range of [0, 359]
		degree = degree >= 0 ? degree % 360 : (degree % 360) + 360;
		if (degree == this.mTargetDegree) {
			return;
		}

		this.mTargetDegree = degree;
		this.mStartDegree = this.mCurrentDegree;
		this.mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis();

		int diff = this.mTargetDegree - this.mCurrentDegree;
		diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]

		// Make it in range [-179, 180]. That's the shorted distance between the
		// two angles
		diff = diff > 180 ? diff - 360 : diff;

		this.mClockwise = diff >= 0;
		this.mAnimationEndTime = this.mAnimationStartTime
				+ ((Math.abs(diff) * 1000) / RotateImageView.ANIMATION_SPEED);

		this.invalidate();
		System.out.println(">>>> degree = " + degree);
		ConfigParameter.DEGREES_ROTATION = degree;
	}

	public void setEdit(final boolean enable) {
		this.editEnable = enable;
	}

	public void setImageHeight(final int h) {
		this.scaleH = h;
	}

	public void setImageWidth(final int w) {
		this.scaleW = w;
	}

	public void setImageX(final int x) {
		this.deltaX = x;
		this.lastDeltaX = x;
	}

	public void setImageY(final int y) {
		this.deltaY = y;
		this.lastDeltaY = y;
	}

	public void setUseFitBoundMode(final boolean values) {
		this.useFitBoundMode = values;
	}

	public void setUseRotateMode(final boolean values) {
		this.useRotateMode = values;
	}

	public void setUseRoundCorner(final boolean values) {
	}

	public void setUseZoomMode(final boolean values) {
		this.useZoomMode = values;
	}

	// public void requestEffect(CreateNewKard instance) {
	// isRequestEffect = true;
	// parentActivity = instance;
	// }

	private float spacing(final MotionEvent event) {
		final float x = event.getX(0) - event.getX(1);
		final float y = event.getY(0) - event.getY(1);
		return (float) Math.sqrt((x * x) + (y * y));
	}

}
